/* Sensor Sonar 
 * 2010 - Interaction Source - Andre Veloso, Koji Pereira e Marcos Machado
 * www.interactionsource.com
 * 
 * Com a utilizacao de codigo dos seguintes projetos:
 *
 * Serial Call-Response - by Tom Igoe. 
 * Sends a byte out the serial port, and reads 3 bytes in.  
 * Thanks to Daniel Shiffman  and Greg Shakar for the improvements.
 * Note: This sketch assumes that the device on the other end of the serial
 * port is going to send a single byte of value 65 (ASCII A) on startup.
 * The sketch waits for that byte, then sends an ASCII A whenever
 * it wants more data. 
 * 
 * RWMidi Processing MIDI Library - 2008 Manuel Odendahl - ruinwesen.com
 * 
 * OSCP5 - oscP5 is a library written by Andreas Schlegel for the programming environment processing. Last update, 05/09/2010.
 * http://www.sojamo.de/libraries/oscP5/
 */

//--- Projeto Domus
// interactionsource.com
// Koji Pereira, Andre Veloso e Marcos Paulo

/*
 Projeto Domus
 interactionsource.com
 Koji Pereira, Andre Veloso e Marcos Paulo

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/

 
 //Recebe os dados seriais via arduino e envia dados via MIDI e OSC para os hosts
 
import processing.serial.*;
import oscP5.*;
import netP5.*;
import rwmidi.*;
 
Serial myPort;                       // The serial port
int[] serialInArray = new int[3];    // Where we'll put what we receive
int serialCount = 0;                 // A count of how many bytes we receive
boolean firstContact = false;        // Whether we've heard from the microcontroller

int CONST_MAX_DIST_SENSOR = 300;    //Distancia maxima do sensor, em cm
int CONST_THRESHOLD_ENVIO = 1;      //tehrshol para coparaca de valor, evitando envio de flutucoes pequenas

int dist1, dist2, dist3 = 0;  //Distancia em cm, capturada da porta serial via arduino
int oldDist1, oldDist2, oldDist3 = 0; //Valor anterior da distancia, para calculo da aceleracao
float accel1, accel2, accel3 = 0;  //valor calculado da aceleracao  dos dados de distancia
float funcData1, funcData2, funcData3 = 0;  //valor calculado apos aplicacao de funcao de suavizacao
float oldFuncData1, oldFuncData2, oldFuncData3 = 0;  //valor calculado apos aplicacao de funcao de suavizacao
int cont1, cont2, cont3 = 0;

OscP5 oscP5;
NetAddress myRemoteLocation;
OscMessage myMessage;

MidiOutput output;
String[] midiPortNames; 
int retMIDI;

float logData = 0;
float espV = 0;
int linData = 0; //DEBUG
int sig = 1;

PFont fontA;

void setup() {

  size (800,600);
  frameRate(60);
  
  fontA = loadFont("SansSerif-30.vlw");
  textFont(fontA, 15);
  
  // Print a list of the serial ports, for debugging purposes:
  println(Serial.list());  
  
  // I know that the first port in the serial list on my mac
  // is always my  FTDI adaptor, so I open Serial.list()[0].
  // On Windows machines, this generally opens COM1.
  // Open whatever port is the one you're using.
  String portName = Serial.list()[0];
  myPort = new Serial(this, portName, 9600);
  
  /* myRemoteLocation is a NetAddress. a NetAddress takes 2 parameters,
   * an ip address and a port number. myRemoteLocation is used as parameter in
   * oscP5.send() when sending osc packets to another computer, device, 
   * application. usage see below. for testing purposes the listening port
   * and the port of the remote location address are the same, hence you will
   * send messages back to this sketch.
   */
  oscP5 = new OscP5(this, 6001); 
  myRemoteLocation = new NetAddress("192.168.2.104", 5000);
  
  //Saida MIDI
  
  midiPortNames = RWMidi.getOutputDeviceNames();
  for (int i=0; i<midiPortNames.length; i++) {
    println("Saida MIDI " + i + " - " + midiPortNames[i]);
  }
  
  output = RWMidi.getOutputDevices()[2].createOutput();
  
}

void draw() {
  background(0);
  
  text("Sensores", width/2, 30);
  
  updateValue(oldDist1, dist1, 1);
  updateValue(oldDist2, dist2, 2);
  updateValue(oldDist3, dist3, 3);
  
  //Simulacao de dados lineares, para testes de funcao
  linData = linData + (1*sig);
  if (linData == 255) {
    sig = -1;
  } else if (linData == 0) {
    sig = 1;
  }
  
  //-------------------------------------------------------------------------------
  //TRANSFORMACOES DE SAIDA - FUNCOES
  //Transformacao segundo uma funcao exponencial ou logaritmica
  //logData = (logN(linData, 10))* 100;
  //espV = exp(linData);
  //logData = linData * espV;
  
  //Transformacao segundo uma funcao de 2o grau   
  //Definindo o crescimento da saida como uma parabola de equacao  y = ax² + bx + c 
  //para um crescimento no eixo X,Y positivo temos c=0, a>0
  //logData = (linData*linData)+((1/2)*linData);
  //logData = logData/150;
    
  //Transformacao segundo uma funcao senoide
  //logData = sin (map(linData, 0, 300, 0, 3.14/2)) * 255;
  //-------------------------------------------------------------------------------
    
  //println("linData: " + linData + " - logData: " + logData + " exp:" + espV);
  
  oldFuncData1 = funcData1;
  oldFuncData2 = funcData2;
  oldFuncData2 = funcData2;
  
  funcData1 = sin (map(accel1, 0, CONST_MAX_DIST_SENSOR, 0, 3.14/2)) * 255;  //aceleracao senoidal
  funcData2 = sin (map(accel2, 0, CONST_MAX_DIST_SENSOR, 0, 3.14/2)) * 255;  //aceleracao senoidal
  funcData3 = sin (map(accel3, 0, CONST_MAX_DIST_SENSOR, 0, 3.14/2)) * 255;  //aceleracao senoidal
  
  //Envia as mensagens somente se os valores atuais sao diferentes dos anteriores por + ou - CONST_THRESHOLD_ENVIO
  //para evitar envio desnecessarios de valores estaticos
  
  if ((funcData1 >oldFuncData1+CONST_THRESHOLD_ENVIO) || (funcData1 <oldFuncData1-CONST_THRESHOLD_ENVIO)){
    //OSC
    enviaMensagemOSC_Float(funcData1, 0, 255, "/sensor1/accell", myRemoteLocation);
    //MIDI
    enviaMensagemMIDI_CC_Float(255-funcData1, 0, 255, 50, 1);   //50 num MSg MIDI CC
  }
  
  if ((funcData2 >oldFuncData2+CONST_THRESHOLD_ENVIO) || (funcData2 <oldFuncData2-CONST_THRESHOLD_ENVIO)){
    //OSC
    enviaMensagemOSC_Float(funcData2, 0, 255, "/sensor2/accell", myRemoteLocation);
    //MIDI
    enviaMensagemMIDI_CC_Float(255-funcData2, 0, 255, 60, 1);   //60 num MSg MIDI CC
  }
  
  if ((funcData3 >oldFuncData3+CONST_THRESHOLD_ENVIO) || (funcData3 <oldFuncData3-CONST_THRESHOLD_ENVIO)){
    //OSC
    enviaMensagemOSC_Float(funcData3, 0, 255, "/sensor3/accell", myRemoteLocation);
    //MIDI
    enviaMensagemMIDI_CC_Float(255-funcData3, 0, 255, 70, 1);   //70 num MSg MIDI CC
  }
 
  // Saida grafica
  //------------------------------------------------------------------------------------
  stroke(255);
  strokeCap(SQUARE);
  strokeWeight(50);
  
  //line(30, height/2, 30, (height/2)-linData );
  //line(100, height/2, 100, (height/2)-logData );
  
  
  line(170, height/2, 170, (height/2)-funcData1);
  text("Sensor1", 145, height/2+30);
  
  line(240, height/2, 240, (height/2)-funcData2);
  text("Sensor2", 215, height/2+30);
  
  line(310, height/2, 310, (height/2)-funcData3);
  text("Sensor3", 285, height/2+30);
  
  //------------------------------------------------------------------------------------ 
 
  
  //delay(10);
  
}

/*
 * Envia uma mensagem OSC contendo um int para o dest especificado
 */
void enviaMensagemOSC_Int(int intVal, int lowRange, int highRange, String oscMessage, NetAddress dest) {
   
       //Converte para a gama aceitavel em OSC, de 0 a 1.0
      float valSend =  map(intVal, lowRange, highRange, 0.0, 1.0);
      valSend = constrain(valSend, 0.0, 1.0);
   
      myMessage = new OscMessage(oscMessage);
      myMessage.add(round(valSend)); // add an int to the osc message 
      oscP5.send(myMessage, dest); 
}

/*
 * Envia uma mensagem OSC contendo um float para o dest especificado
 */
void enviaMensagemOSC_Float(float floatVal, int lowRange, int highRange, String oscMessage, NetAddress dest) {
   
      //Converte para a gama aceitavel em OSC, de 0 a 1.0
      float valSend =  map(floatVal, lowRange, highRange, 0.0, 1.0);
      valSend = constrain(valSend, 0.0, 1.0);
    
      myMessage = new OscMessage(oscMessage);
      myMessage.add(floatVal); // add an int to the osc message 
      oscP5.send(myMessage, dest); 
  
}

/*
 * Envia uma mensagem MIDI a partir  de  um int para o dest especificado
 */
void enviaMensagemMIDI_CC_Int(int intVal, int lowRange, int highRange, int ccNumber, int midiChn) {  
      
      float valSend =  map(intVal, lowRange, highRange, 0, 127);
      valSend = constrain(valSend, 0, 127);  
      retMIDI = output.sendController(midiChn, ccNumber, round(valSend) );
  
}

/*
 * Envia uma mensagem MIDI a partir  de  um float para o dest especificado
 */
void enviaMensagemMIDI_CC_Float(float floatVal, int lowRange, int highRange, int ccNumber, int midiChn) {  
  
      float valSend =  map(floatVal, lowRange, highRange, 0, 127);
      valSend = constrain(valSend, 0, 127);  
      retMIDI = output.sendController(midiChn, ccNumber, round(valSend) );
  
}

// Calculates the base-10 logarithm of a number
float log10 (int x) {
  return (log(x) / log(10));
}


// Calculates the base-n logarithm of a number
float logN (int x, float base) {
  return (log(x) / log(base));
}


/*
 *
 */
void updateValue(int valOld, int valueNew, int nSensor) {
  
  int nval = abs(valueNew - valOld);
  
  switch(nSensor) {
    case 1:   
      
      if (accel1 == valueNew) {
         return; 
      }
     
      if (cont1 < nval) {
        if (accel1 < valueNew) {
          accel1 += 1.1 * (1 + (nval/20));  //println ( "soma - valueNew: " + valueNew + " valOld: " + valOld );
        } else if (accel1 > valueNew){
          accel1 -= 1.1 * (1 + (nval/20));  //println ( "subtrai - valueNew: " + valueNew + " valOld: " + valOld );
        }
        cont1++;
      } 
      
      if (cont1 >=nval) {
        cont1 = 0;
    
      }
    break;
    
    case 2:   
      
      if (accel2 == valueNew) {
         return; 
      }
     
      if (cont2 < nval) {
        if (accel2 < valueNew) {
          accel2 += 1.1 * (1 + (nval/20));  //println ( "soma - valueNew: " + valueNew + " valOld: " + valOld );
        } else if (accel2 > valueNew){
          accel2 -= 1.1 * (1 + (nval/20));  //println ( "subtrai - valueNew: " + valueNew + " valOld: " + valOld );
        }
        cont2++;
      } 
      
      if (cont2 >=nval) {
        cont2 = 0;
    
      }
    break;
    
    case 3:   
      
      if (accel3 == valueNew) {
         return; 
      }
     
      if (cont3 < nval) {
        if (accel3 < valueNew) {
          accel3 += 1.1 * (1 + (nval/20));  //println ( "soma - valueNew: " + valueNew + " valOld: " + valOld );
        } else if (accel3 > valueNew){
          accel3 -= 1.1 * (1 + (nval/20));  //println ( "subtrai - valueNew: " + valueNew + " valOld: " + valOld );
        }
        cont3++;
      } 
      
      if (cont3 >=nval) {
        cont3 = 0;
    
      }
    break;
    
    
  }
  
}

/*
 * trata os eventos de recebimento de dados da serial
 */
void serialEvent(Serial myPort) {
  
  // read a byte from the serial port:
  int inByte = myPort.read();
  
  //println("Comunicacao serial em inicio");
  
  // if this is the first byte received, and it's an A,
  // clear the serial buffer and note that you've
  // had first contact from the microcontroller. 
  // Otherwise, add the incoming byte to the array:
  if (firstContact == false) {
    if (inByte == 'A') { 
      myPort.clear();          // clear the serial port buffer
      firstContact = true;     // you've had first contact from the microcontroller
      myPort.write('A');       // ask for more
    } 
  } 
  else {
    // Add the latest byte from the serial port to array:
    serialInArray[serialCount] = inByte;
    serialCount++;

   println("Comunicacao serial em progresso - count :" + serialCount );

    // If we have 3 bytes:
    if (serialCount > 2 ) {
        
      oldDist1 = dist1;
      oldDist2 = dist2;
      oldDist3 = dist3;
      
      
      dist1 = serialInArray[0];
      dist2 = serialInArray[1];
      dist3 = serialInArray[2];
                           
      // print the values (for debugging purposes only):
      println("Distancia 1:" + dist1 + " - Distancia 2:" + dist1 + " - Distancia 3: " + dist3);

      // Send a capital A to request new sensor readings:
      myPort.write('A');
      // Reset serialCount:
      serialCount = 0;
    }
  }
}


